package cn.congine.wizarpos.mall.action;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jdom.JDOMException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.alibaba.fastjson.JSONObject;

import cn.congine.wizarpos.mall.common.RespMessage;
import cn.congine.wizarpos.mall.model.SaleOrder;
import cn.congine.wizarpos.mall.model.SaleOrderItem;
import cn.congine.wizarpos.mall.model.WxOrder;
import cn.congine.wizarpos.mall.model.WxOrderDetail;
import cn.congine.wizarpos.mall.model.open.AccessTokenInput;
import cn.congine.wizarpos.mall.model.open.CateringSaleOrderPayInput;
import cn.congine.wizarpos.mall.model.open.CateringSaleOrderPayOutput;
import cn.congine.wizarpos.mall.model.open.CommonResultOutput;
import cn.congine.wizarpos.mall.model.open.GetEmployeesOutput;
import cn.congine.wizarpos.mall.model.open.GetEmployeeslInput;
import cn.congine.wizarpos.mall.model.open.GetOrderNoOutput;
import cn.congine.wizarpos.mall.model.open.GetShopInfoInput;
import cn.congine.wizarpos.mall.model.open.GetShopInfoOutput;
import cn.congine.wizarpos.mall.model.open.MemberCardRechargeInput;
import cn.congine.wizarpos.mall.model.open.MemberCardRechargeOutput;
import cn.congine.wizarpos.mall.model.open.Merchandise;
import cn.congine.wizarpos.mall.model.open.MerchandiseSyncInput;
import cn.congine.wizarpos.mall.model.open.MerchandiseSyncOutput;
import cn.congine.wizarpos.mall.model.open.MerchantDef;
import cn.congine.wizarpos.mall.model.open.MerchantDefPayInfoInput;
import cn.congine.wizarpos.mall.model.open.MerchantDefPayInfoOutput;
import cn.congine.wizarpos.mall.model.open.MerchantDefPayInfoOutput.Result;
import cn.congine.wizarpos.mall.model.open.MerchantDefQueryInput;
import cn.congine.wizarpos.mall.model.open.MerchantDefQueryOutput;
import cn.congine.wizarpos.mall.model.open.Payment;
import cn.congine.wizarpos.mall.model.open.QrcodeToJssdkPayInput;
import cn.congine.wizarpos.mall.model.open.RechargeMarketInfo;
import cn.congine.wizarpos.mall.model.open.SaleOrderItemView;
import cn.congine.wizarpos.mall.model.open.ShopInfoPair;
import cn.congine.wizarpos.mall.model.open.TerminalQueryInput;
import cn.congine.wizarpos.mall.model.open.TerminalQueryOutput;
import cn.congine.wizarpos.mall.model.open.TicketUseInput;
import cn.congine.wizarpos.mall.model.open.TicketUseOutput;
import cn.congine.wizarpos.mall.model.open.UpdateOrderInfoInput;
import cn.congine.wizarpos.mall.model.open.UpdateOrderInfoOutput;
import cn.congine.wizarpos.mall.model.open.WechartOrder;
import cn.congine.wizarpos.mall.model.open.WechartOrderDetailInput;
import cn.congine.wizarpos.mall.model.open.WechartOrderOutput;
import cn.congine.wizarpos.mall.model.open.WxJsApiPayUpdateInput;
import cn.congine.wizarpos.mall.model.open.WxJsApiPayUpdateOutput;
import cn.congine.wizarpos.mall.model.open.WxPayUpdateInput;
import cn.congine.wizarpos.mall.model.open.WxPayUpdateOutput;
import cn.congine.wizarpos.mall.model.open.WxSaleOrderQueryInput;
import cn.congine.wizarpos.mall.model.open.WxSaleOrderQueryOutput;
import cn.congine.wizarpos.mall.model.open.WxSaleOrderQueryV1_1Output;
import cn.congine.wizarpos.mall.service.impl.WPosHttpClient;
import cn.congine.wizarpos.mall.service.impl.WizarposOpenRestClient;
import cn.congine.wizarpos.mall.service.impl.WxHttpClient;
import cn.congine.wizarpos.mall.tenpay.ChannelJsSdkPaymentPackage;
import cn.congine.wizarpos.mall.tenpay.ChannelPaymentPackage;
import cn.congine.wizarpos.mall.tenpay.JsSdkPaymentPackage;
import cn.congine.wizarpos.mall.tenpay.PaymentPackage;
import cn.congine.wizarpos.mall.tenpay.QQPrePayRequest;
import cn.congine.wizarpos.mall.tenpay.WechatPayment;
import cn.congine.wizarpos.mall.tenpay.util.XMLUtil;
import cn.congine.wizarpos.mall.utils.DES;
import cn.congine.wizarpos.mall.utils.Function;
import cn.congine.wizarpos.mall.utils.SysConstants;
import cn.congine.wizarpos.mall.utils.Utils;
import cn.congine.wizarpos.mall.utils.WxSignUtil;

@Controller
@RequestMapping(value = "/tenpay")
public class PayAction {
	private final Log log = LogFactory.getLog(getClass());

	private final String unifiedorder = "https://api.mch.weixin.qq.com/pay/unifiedorder";
	private final String QQ_PREPAY = "http://myun.tenpay.com/cgi-bin/wappayv2.0/wappay_init.cgi";

	@Resource(name = "messageSource")
	private MessageSource messageSource;

	@Autowired
	private WxHttpClient wxHttpClient;

	@Autowired
	private WPosHttpClient wPosHttpClient;
	
	@Autowired
	private WizarposOpenRestClient wizarposOpenRestClient;
	
	/**
	 * 线上统一支付（微信，支付宝，QQ钱包）
	 * 
	 * @param httpRequest
	 * @return
	 */
	@RequestMapping(value = "/unifiedpay", method = RequestMethod.GET)
	public ModelAndView unifiedPay(@RequestParam(value = "uid", required = true) String uid) {
		try {
			uid = URLEncoder.encode(uid,"utf-8");
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return new ModelAndView("error").addObject("err_message", "系统错误");	 
		}
		return new ModelAndView("jsapipay/unifiedpay").addObject("uid", uid);
	}
	
	@RequestMapping(value = "/getwxpay", method = RequestMethod.GET)
	@ResponseBody
	public RespMessage getWxPay(@RequestParam(value = "uid", required = true) String uid) {
		RespMessage res = new RespMessage();
		// des解密 id 取得 mid,sn
		String mid = null;
		String sn = null;
		String orderId = null;
		String plainText = null;
		try {
			plainText = DES.decryptDES(uid, SysConstants.ENCRYPT_DECRYPT_KEY);
		} catch (Exception e) {
			e.printStackTrace();
			log.error("解密失败");
			log.error(uid);
			log.error(plainText);
			res.setCode(-1);
			res.setMessage("系统错误");
			return res;	
		}

		JSONObject param = JSONObject.parseObject(plainText);
		mid = param.getString("mid");
		sn = param.getString("sn");
		orderId = param.getString("orderId");
		
		QrcodeToJssdkPayInput input = new QrcodeToJssdkPayInput();
		input.setMid(mid);
		input.setSn(sn);
		input.setOrderId(orderId);
		input.setShortUrl("N");
		CommonResultOutput output = (CommonResultOutput) wizarposOpenRestClient.post(input, "/weixin/jssdkpay/qrcode", CommonResultOutput.class);
		if (output == null) {//系统错误
			res.setCode(-1);
			res.setMessage("系统错误");
			log.warn("请求开放平台获取微信支付短链接失败， mid=" + mid + ", orderId=" + orderId);
			return res;			
		} else if (!"0".equals(output.getCode())) {//60020 非法订单号
			res.setCode(-1);
			res.setMessage("系统错误");
			log.warn("请求开放平台获取微信支付短链接失败， mid=" + mid + ", orderId=" + orderId);
			return res;			
		} else {
			res.setCode(0);
			res.setMessage("success");
			res.setObj(output.getResult());
			log.debug("请求开放平获取微信支付短链接成功， url=" + mid + ", orderId=" + orderId + ", url=" + output.getResult());
			return res;			
		}

	}
	/**
	 * qq扫码H5支付页面
	 * 
	 * @param id
	 * @param httpRequest
	 * @return
	 */
	@RequestMapping(value = "/pay_qq")
	public ModelAndView qqPay(@RequestParam(required = true) String uid,
			HttpServletRequest httpRequest) {
		Map<String, Object> data = new HashMap<String, Object>();
		// des解密 id 取得 mid,sn
		String mid = null;
		String sn = null;
		String orderId = null;
		String plainText = null;
		try {
			plainText = DES.decryptDES(uid, SysConstants.ENCRYPT_DECRYPT_KEY);
		} catch (Exception e) {
			e.printStackTrace();
			log.error("解密失败");
			log.error(uid);
			log.error(plainText);
			data.put("err_message", "系统错误");
			return new ModelAndView("error", data);
		}

		JSONObject param = JSONObject.parseObject(plainText);
		mid = param.getString("mid");
		sn = param.getString("sn");
		orderId = param.getString("orderId");
		
		// 查询商户信息
		MerchantDefQueryInput merchantDefQueryInput = new MerchantDefQueryInput();
		merchantDefQueryInput.setMid(mid);
		MerchantDefQueryOutput merchantDefouptut = (MerchantDefQueryOutput) wizarposOpenRestClient.post(merchantDefQueryInput, "/merchantdef/info", MerchantDefQueryOutput.class);
		if (merchantDefouptut == null) {
			log.error("请求开放平台查询商户信息接口失败");
			log.error("mid="+mid);
			log.error("sn="+sn);
			log.error("orderId="+orderId);
			data.put("err_message", "系统错误");
			return new ModelAndView("error", data);
		} else if (SysConstants.OPEN_SUCCESS.equals(merchantDefouptut.getCode())) {
			data.put("merchant", merchantDefouptut.getResult());
			data.put("mid", mid);
			data.put("sn", sn);
		} else {
			log.error("code：" + merchantDefouptut.getCode());
			log.error("message：" + merchantDefouptut.getMessage());
			log.error("mid="+mid);
			log.error("sn="+sn);
			log.error("orderId="+orderId);
			data.put("err_message", "系统异常");
			return new ModelAndView("error", data);
		}
		// 查询商户信息
		TerminalQueryInput terminalQueryInput = new TerminalQueryInput();
		terminalQueryInput.setMid(mid);
		terminalQueryInput.setSn(sn);
		TerminalQueryOutput terminalOuptut = (TerminalQueryOutput) wizarposOpenRestClient.post(terminalQueryInput, "/merchantdef/terminal/query", TerminalQueryOutput.class);
		if (terminalOuptut == null) {
			log.error("请求开放平台查询终端信息接口失败");
			log.error("mid="+mid);
			log.error("sn="+sn);
			log.error("orderId="+orderId);
			data.put("err_message", "系统错误");
			return new ModelAndView("error", data);
		} else if (SysConstants.OPEN_SUCCESS.equals(terminalOuptut.getCode())) {
			data.put("storeName", terminalOuptut.getResult().getStoreName());
			data.put("mid", mid);
			data.put("sn", sn);
		} else {
			log.error("code：" + terminalOuptut.getCode());
			log.error("message：" + terminalOuptut.getMessage());
			log.error("mid="+mid);
			log.error("sn="+sn);
			log.error("orderId="+orderId);
			data.put("err_message", "系统异常");
			return new ModelAndView("error", data);
		}
		
		if (!StringUtils.isEmpty(orderId) && !"null".equals(orderId)) {
			WechartOrderDetailInput orderDetailInput = new WechartOrderDetailInput();
			orderDetailInput.setMid(mid);
			orderDetailInput.setOrderId(orderId);
			WechartOrderOutput orderOutput = (WechartOrderOutput) wizarposOpenRestClient.post(orderDetailInput, "/order/detail", WechartOrderOutput.class);
			if (orderOutput == null) {
				log.error("请求开放平台查询微信订单详情失败");
				log.error("mid="+mid);
				log.error("sn="+sn);
				log.error("orderId="+orderId);
				data.put("err_message", "系统错误");
				return new ModelAndView("error", data);
			} else if (SysConstants.OPEN_SUCCESS.equals(orderOutput.getCode())) {
				WechartOrder order = orderOutput.getResult();
				if ("5".equals(order.getStatus())) {
					data.put("err_message", "订单已失效");
					return new ModelAndView("error", data);
				}
				if ("1".equals(order.getPayStatus())) {
					data.put("err_message", "订单已支付");
					return new ModelAndView("error", data);
				}
				data.put("orderId", orderId);
				String amount = orderOutput.getResult().getAmount();
				data.put("amount", amount);
				data.put("showAmount", Utils.formatAmount(amount));
			} else {
				log.error("请求开放平台查询微信订单详情失败");
				log.error("mid="+mid);
				log.error("sn="+sn);
				log.error("orderId="+orderId);
				log.error(orderOutput.getMessage());
				data.put("err_message", "系统错误");
				return new ModelAndView("error", data);
			}
		}
		
		return new ModelAndView("jsapipay/pay_qq", data);
	}
	
	/**
	 * qqjsapi支付，生成qq支付的的tokenId
	 * 
	 * @param mid
	 * @param sn
	 * @param httpRequest
	 * @return
	 */
	@RequestMapping(value = "/qqprepay")
	@ResponseBody
	public RespMessage getQqPrepayTokenId(
			@RequestParam(required = true) String mid,
			@RequestParam(required = true) String sn,
			@RequestParam(required = false) String orderId,
			@RequestParam(required = true) String amount,
			HttpServletRequest httpRequest) {
		RespMessage res = new RespMessage();
	
		// 校验金额
		int tranAmount = 0;
		try {
			tranAmount = Integer.valueOf(amount);
		} catch (NumberFormatException e) {
			e.printStackTrace();
			res.setCode(-1);
			res.setMessage("金额非法：" + amount);
			log.warn("金额非法：" + amount);
			return res;		
		}
		
		// 第三方订单号
		GetOrderNoOutput.Result rst = wPosHttpClient.getOrderNo(mid, mid, SysConstants.QQ, "", tranAmount, 0, tranAmount, "", "");
		if (rst == null) {
			res.setCode(-1);
			res.setMessage("系统错误，操作失败，请重试");
			log.error("请求开放平台获取订单编号失败， orderId=" + orderId);
			return res;			
		}
		
		String orderNo = rst.getOrderNo();
		if (StringUtils.isEmpty(orderId)) {
			res.setMessage(orderNo);
		} else {
			res.setMessage(orderId);
		};
		
		// 获取token_id的request
		QQPrePayRequest qqPrePayRequest = new QQPrePayRequest();
		qqPrePayRequest.setSp_billno(orderNo);
		qqPrePayRequest.setTotal_fee(tranAmount);
		String url = getBaseURL(httpRequest) + "/tenpay/qq-h5-notify";
		qqPrePayRequest.setNotify_url(url);
		qqPrePayRequest.setTime_start(Function.formatDate("yyyymmddhhmmss"));
		qqPrePayRequest.setTime_start(Function.formatDate(Function.addMinute(new Date(), 3), "yyyymmddhhmmss"));//
		// 附加数据(支付成功后会返回)
		JSONObject attach = new JSONObject();
		attach.put("mid", mid);
		attach.put("sn", sn);
		attach.put("orderId", orderId);

		qqPrePayRequest.setAttach(attach.toString());
		// 查询商户信息
		MerchantDefQueryInput merchantDefQueryInput = new MerchantDefQueryInput();
		merchantDefQueryInput.setMid(mid);
		MerchantDefQueryOutput merchantDefouptut = (MerchantDefQueryOutput) wizarposOpenRestClient.post(merchantDefQueryInput, "/merchantdef/info", MerchantDefQueryOutput.class);
		if (merchantDefouptut != null && SysConstants.OPEN_SUCCESS.equals(merchantDefouptut.getCode())) {
			qqPrePayRequest.setDesc(merchantDefouptut.getResult().getMerchantName());
		} else {
			qqPrePayRequest.setDesc("慧银QQ钱包支付");
		}

		// 请求开放平台获取商户支付信息数据
		MerchantDefPayInfoInput merchantDefPayInfoInput = new MerchantDefPayInfoInput();
		merchantDefPayInfoInput.setMid(mid);
		merchantDefPayInfoInput.setPayType("3");
		MerchantDefPayInfoOutput merchantDefPayInfoOutput = (MerchantDefPayInfoOutput) wizarposOpenRestClient.post(merchantDefPayInfoInput, "/merchantdef/payInfo", MerchantDefPayInfoOutput.class);				
		if (merchantDefPayInfoOutput == null
				|| !"0".equals(merchantDefPayInfoOutput.getCode())
				|| merchantDefPayInfoOutput.getResult() == null) {
			if (merchantDefPayInfoOutput == null) {
				log.warn("merchantDefPayInfoOutput is null");
			} else {
				log.warn(merchantDefPayInfoOutput.getMessage());
			}
			res.setCode(-1);
			res.setMessage("系统异常，无法发起支付");
			log.error("请求开发平台获取商户支付信息失败，无法发起支付");
			return res;
		}	
		
		Result result = merchantDefPayInfoOutput.getResult();
		// 微信商户平台ID
		String partner = null;
		String payKey = null;
		// 连接类型1.大商户2渠道3直连
		if ("2".equals(result.getConnectionType())) {// 2渠道
			partner = result.getChannelPartnerId();
			// 子商户号
			String subPartner = result.getMerchantPartnerId();
			payKey = result.getChannelAppKey();
			// 交易商户支付信息
			if (StringUtils.isEmpty(partner) 
					|| StringUtils.isEmpty(subPartner)
					|| StringUtils.isEmpty(payKey)) {
				res.setCode(-1);
				res.setMessage("商户信息不完整，无法发起支付");
				log.error("公众号api支付，渠道商户信息不完整，无法发起支付，mid="+mid);
				log.warn(result);
				return res;
			}
			qqPrePayRequest.setSign_sp_id(partner);
			qqPrePayRequest.setBargainor_id(subPartner);
		} else { // 3直连 
			partner = result.getMerchantPartnerId();
			payKey = result.getMerchantAppKey();

			// 交易商户支付信息
			if (StringUtils.isEmpty(partner)
					|| StringUtils.isEmpty(payKey)) {
				res.setCode(-1);
				res.setMessage("商户信息不完整，无法发起支付");
				log.error("公众号api支付，直连商户信息不完整，无法发起支付,mid="+mid);
				log.warn(result);
				return res;
			}
			qqPrePayRequest.setBargainor_id(partner);
		}
		
		// 请求签名
		qqPrePayRequest.toSign(payKey);
		
		String querystring = QQ_PREPAY + "?" + qqPrePayRequest.getParams();
		
		Map<String, String> respMap = null;
		String resp = null;
		try {
			resp = wxHttpClient.get(querystring);
			respMap = XMLUtil.doXMLParse(resp);
		} catch (Exception e) {
			log.error(querystring);
			log.error(resp);
			log.error(e.getMessage());
			return null;
		}
		
		String tokenId = respMap.get("token_id");
		
		if (tokenId == null) {
			log.error(querystring);
			log.error(resp);
			res.setCode(-1);
			res.setMessage("请求异常，支付请求失败");
			log.error("QQ公众号api支付，请求异常，支付请求失败, token_id = null");
			return res;
		}
		
		log.debug(querystring);
		log.debug(resp);

		// 2.支付参数
		JSONObject obj = new JSONObject();
		obj.put("tokenId", tokenId);
		obj.put("pubAcc", "uin3362416929");
		obj.put("pubAccHint", "慧银信息科技有限公司");
		res.setObj(obj);
		
		return res;
	}
		
	/**
	 * QQ支付回调
	 * @param request
	 * @param response
	 * @throws Exception
	 */
	@RequestMapping(value = "/qq-h5-notify")
	public void qqH5Notify(HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		Map<String, Object> resMap = request.getParameterMap();
		if (resMap == null) {
			log.equals("resMap is null");
			sendToCFT(response,	"fail");
		}
		// 支付结果
		String pay_result = ((String[]) resMap.get("pay_result"))[0];
		
		// 判断签名及结果
		if ("0".equals(pay_result)) {
			// 商户订单号
			String payOrderNo = ((String[]) resMap.get("sp_billno"))[0];
			// 金额,以分为单位
			String total_fee = ((String[]) resMap.get("total_fee"))[0];
			// 支付订单号
			String transactionId = ((String[]) resMap.get("transaction_id"))[0];
			// 用户号
			String openId = ((String[]) resMap.get("purchase_alias"))[0];

			String attach = ((String[]) resMap.get("attach"))[0];
			JSONObject attachJson = JSONObject.parseObject(attach);
			String mid = attachJson.getString("mid");
			String sn = attachJson.getString("sn");
			// 微信订单号
			String orderId = attachJson.getString("orderId");
			
			//请求开放平台微信jssdk支付更新接口
			WxJsApiPayUpdateInput input = new WxJsApiPayUpdateInput();
			input.setOrderId(orderId);
			input.setPayOrderNo(payOrderNo);
			input.setTotalFee(total_fee);
			input.setTransactionId(transactionId);
			input.setOpenId(openId);
			input.setMid(mid);
			input.setSn(sn);
			input.setEnvType("QQ");
			WxJsApiPayUpdateOutput output = (WxJsApiPayUpdateOutput) wizarposOpenRestClient.post(input, "/wxshop/wxJsApiPayUpdate", WxJsApiPayUpdateOutput.class);
			
			// 给财付通系统发送成功信息，财付通系统收到此结果后不再进行后续通知
			if ("60034".equals(output.getCode())) {//订单重复支付			
				log.error("Bad request:重复的支付交易-订单[" + orderId + "]已支付");
				sendToCFT(response,	"success");
				return;
			}
			if ("0".equals(output.getCode())) {//操作成功
				log.info("订单[" + payOrderNo + "]支付成功");
				sendToCFT(response,	"success");
				return;
			} else {
				log.error("订单[" + payOrderNo + "]保存失败，" + output.getMessage());
				sendToCFT(response,	"fail");
				return;
			}
		} else {
			log.error("pay_result = " + pay_result);
			sendToCFT(response, "success");
			return;
		}
	}
	
	@RequestMapping(value = "/pay")
	public ModelAndView toPayPage(@RequestParam(required = true) String mid,
			@RequestParam(required = true) String orderId,
			@RequestParam(required = true) String dispatchType,
			HttpServletRequest httpRequest) {
		String openId = (String) httpRequest.getSession().getAttribute("openId");
		if (StringUtils.isEmpty(openId)) {//session过期，跳转到登录注册页面
			return new ModelAndView("forward:/vip/login");	
		}
		
		Map<String, Object> data = new HashMap<String, Object>();	
		UpdateOrderInfoInput input = new UpdateOrderInfoInput();
		input.setMid(mid);
		input.setOrderId(orderId);
		input.setDispatchType(dispatchType);
		UpdateOrderInfoOutput output = (UpdateOrderInfoOutput) wizarposOpenRestClient.post(input, "/order/update_orderinfo",UpdateOrderInfoOutput.class);
		log.debug(output.getCode());
		log.debug(output.getMessage());
		
		data.put("orderId", orderId);
		data.put("mid", mid);
		return new ModelAndView("tenpay", data);
	}
	
	// 微信支付v3.3.7版本
	@RequestMapping(value = "/message", method = RequestMethod.GET)
	@ResponseBody
	public RespMessage getPaymentMessage(
			@RequestParam(required = true) String mid,//add xudongdong
			@RequestParam(required = true) String orderId,
			HttpServletRequest request) {
		RespMessage res = new RespMessage();
		
		WechartOrderDetailInput wechartOrderDetailInput = new WechartOrderDetailInput();
		wechartOrderDetailInput.setMid(mid);
		wechartOrderDetailInput.setOrderId(orderId);
		WechartOrderOutput wechartOrderOutput = (WechartOrderOutput) wizarposOpenRestClient.post(wechartOrderDetailInput, "/order/detail", WechartOrderOutput.class);

		if (wechartOrderOutput == null) {//系统错误
			res.setCode(-1);
			res.setMessage("系统错误，操作失败，请重试");
			log.warn("请求开放平台获取订单信息失败， mid=" + mid + ", orderId=" + orderId);
			return res;			
		}		
		if ("60020".equals(wechartOrderOutput.getCode())) {//60020 非法订单号
			res.setCode(-1);
			res.setMessage("无效支付请求：非法订单号");
			log.warn("无效支付请求,非法订单号: " + orderId);
			return res;			
		}		
		WechartOrder orderTemp = wechartOrderOutput.getResult();					
		WxOrder order = new WxOrder();
		try {
			BeanUtils.copyProperties(order, orderTemp);
		} catch (IllegalAccessException | InvocationTargetException e) {
			//do nothing
		}		
		List<WxOrderDetail> orderItems = orderTemp.getOrderDetail();

		// 回调地址
		String notifyUrl = getBaseURL(request) + "/tenpay/pay-notify";
		log.debug(notifyUrl);

		// 请求开放平台获取商户支付信息数据
		MerchantDefPayInfoInput merchantDefPayInfoInput = new MerchantDefPayInfoInput();
		merchantDefPayInfoInput.setMid(mid);
		merchantDefPayInfoInput.setPayType("1");
		MerchantDefPayInfoOutput merchantDefPayInfoOutput = (MerchantDefPayInfoOutput) wizarposOpenRestClient.post(merchantDefPayInfoInput, "/merchantdef/payInfo", MerchantDefPayInfoOutput.class);				
		if (merchantDefPayInfoOutput == null
				|| !"0".equals(merchantDefPayInfoOutput.getCode())
				|| merchantDefPayInfoOutput.getResult() == null) {
			res.setCode(-1);
			res.setMessage("系统异常，无法发起支付");
			log.error("请求开发平台获取商户支付信息失败，无法发起支付");
			log.warn(merchantDefPayInfoOutput.getMessage());
			return res;
		}	
		Result result = merchantDefPayInfoOutput.getResult();
		// 微信商户平台ID
		String partner = null;
		// 微信商户平台支付密钥
		String partnerKey = null;
		// 微信公众平台应用ID
		String appid = null;
		// 预支付回话标识
		String prepay_id = null;
		
		// 连接类型1.大商户2渠道3直连
		if ("2".equals(result.getConnectionType())) {// 2渠道
			partnerKey = result.getChannelAppKey();
			appid = result.getChannelAppId();
			partner = result.getChannelPartnerId();
			// 子商户公众账号ID
			String subAppId = result.getMerchantAppId();
			// 子商户号
			String subPartner = result.getMerchantPartnerId();
			// 交易商户支付信息
			if (StringUtils.isEmpty(appid) || StringUtils.isEmpty(partner)
					|| StringUtils.isEmpty(subAppId)
					|| StringUtils.isEmpty(subPartner)
					|| StringUtils.isEmpty(partnerKey)) {
				res.setCode(-1);
				res.setMessage("商户信息不完整，无法发起支付");
				log.error("渠道商户信息不完整，无法发起支付，mid="+mid);
				log.error(result);
				return res;
			}
			// 1.生成预支付回话标识
			prepay_id = getChannelPrepay_id(appid, partner, partnerKey, subAppId, subPartner, notifyUrl,
					request.getRemoteAddr(), order, orderItems);		
			
		} else {// 3直连 （此处：1大商户情形不存在）
			partnerKey = result.getMerchantAppKey();
			appid = result.getMerchantAppId();
			partner = result.getMerchantPartnerId();
			// 交易商户支付信息
			if (StringUtils.isEmpty(appid) || StringUtils.isEmpty(partner) || StringUtils.isEmpty(partnerKey)) {
				res.setCode(-1);
				res.setMessage("商户信息不完整，无法发起支付");
				log.error("直连商户信息不完整，无法发起支付");
				return res;
			}
			// 1.生成预支付回话标识
			prepay_id = getPrepay_id(appid, partner, partnerKey, notifyUrl,
					request.getRemoteAddr(), order, orderItems);
		}
		
		if (prepay_id == null) {
			res.setCode(-1);
			res.setMessage("请求异常，支付请求失败");
			log.error("请求异常，支付请求失败, prepay_id = null");
			return res;
		}	

		// 2.支付参数
		WechatPayment pm = new WechatPayment();
		pm.setPaymentPackage("prepay_id=" + prepay_id);
		pm.sign(appid, partnerKey);

		res.setObj(pm);
		return res;
	}

	/**
	 * jssdk支付页面(生成prepay_id)
	 * 
	 * @param mid
	 * @param sn
	 * @param httpRequest
	 * @return
	 */
	@RequestMapping(value = "/recharge-message")
	@ResponseBody
	public RespMessage getRechargeMessage(
			@RequestParam(required = true) String mid,
			@RequestParam(required = true) String amount,
			@RequestParam(required = false) String operatorId,
			@RequestParam(required = false) String shopMid,
			HttpServletRequest httpRequest) {
		RespMessage res = new RespMessage();
		String openId = (String) httpRequest.getSession().getAttribute("openId");
		if (StringUtils.isEmpty(openId)) {//session过期
			res.setCode(-2);
			res.setMessage("session过期，请重新扫码完成支付");
			return res;	
		}
		// 校验金额
		int tranAmount = 0;
		try {
			tranAmount = Integer.valueOf(amount);
		} catch (NumberFormatException e) {
			e.printStackTrace();
			res.setCode(-1);
			res.setMessage("金额非法：" + amount);
			log.warn("金额非法：" + amount);
			return res;		
		}
		if (StringUtils.isEmpty(shopMid)) {
			shopMid = mid;
		}
		// 第三方订单号
		GetOrderNoOutput.Result rst = wPosHttpClient.getOrderNo(mid, shopMid, SysConstants.WX, "", tranAmount, 0, tranAmount, "", "");
		if (rst == null) {
			res.setCode(-1);
			res.setMessage("系统错误，操作失败，请重试");
			log.error("请求开放平台获取订单编号失败， mid=" + mid);
			return res;			
		}
		String orderId = rst.getOrderNo();
		res.setMessage(orderId);
		// 产生临时订单（获取prepay_id用）
		WxOrder order = new WxOrder();
		order.setOpenId(openId);
		order.setOrderId(orderId);
		order.setAmount(tranAmount);
		List<WxOrderDetail> orderItems = new ArrayList<WxOrderDetail>();
		WxOrderDetail wxOrderDetail = new WxOrderDetail();
		// 查询商户信息
		MerchantDefQueryInput merchantDefQueryInput = new MerchantDefQueryInput();
		merchantDefQueryInput.setMid(mid);
		MerchantDefQueryOutput merchantDefouptut = (MerchantDefQueryOutput) wizarposOpenRestClient.post(merchantDefQueryInput, "/merchantdef/info", MerchantDefQueryOutput.class);
		if (merchantDefouptut != null && SysConstants.OPEN_SUCCESS.equals(merchantDefouptut.getCode())) {
			wxOrderDetail.setProductName(merchantDefouptut.getResult().getMerchantName());
		} else {
			if (merchantDefouptut ==null) {
				log.error("请求开放平台商户信息接口失败");
			} else {
				log.error("code：" + merchantDefouptut.getCode());
				log.error("message：" + merchantDefouptut.getMessage());
			}
			wxOrderDetail.setProductName("会员卡充值");
		}
		orderItems.add(wxOrderDetail);
		
		// 附加数据(支付成功后会返回)
		JSONObject attach = new JSONObject();
		attach.put("mid", mid);
		attach.put("operatorId", operatorId);
		attach.put("shopMid", shopMid);
		
		// 回调地址
		String notifyUrl =  getBaseURL(httpRequest) + "/tenpay/recharge-pay-notify";
		log.debug(notifyUrl);

		// 请求开放平台获取商户支付信息数据
		MerchantDefPayInfoInput merchantDefPayInfoInput = new MerchantDefPayInfoInput();
		merchantDefPayInfoInput.setMid(mid);
		merchantDefPayInfoInput.setPayType("1");
		MerchantDefPayInfoOutput merchantDefPayInfoOutput = (MerchantDefPayInfoOutput) wizarposOpenRestClient.post(merchantDefPayInfoInput, "/merchantdef/payInfo", MerchantDefPayInfoOutput.class);				
		if (merchantDefPayInfoOutput == null
				|| !"0".equals(merchantDefPayInfoOutput.getCode())
				|| merchantDefPayInfoOutput.getResult() == null) {
			log.warn(merchantDefPayInfoOutput.getMessage());
			res.setCode(-1);
			res.setMessage("系统异常，无法发起支付");
			log.error("请求开发平台获取商户支付信息失败，无法发起支付");
			return res;
		}	
		Result result = merchantDefPayInfoOutput.getResult();
		// 微信商户平台ID
		String partner = null;
		// 微信商户平台支付密钥
		String partnerKey = null;
		// 微信公众平台应用ID
		String appid = null;
		// 预支付回话标识
		String prepay_id = null;
		
		// 连接类型1.大商户2渠道3直连
		if ("2".equals(result.getConnectionType())) {// 2渠道
			partnerKey = result.getChannelAppKey();
			appid = result.getChannelAppId();
			partner = result.getChannelPartnerId();
			// 子商户公众账号ID
			String subAppId = result.getMerchantAppId();
			// 子商户号
			String subPartner = result.getMerchantPartnerId();
			// 交易商户支付信息
			if (StringUtils.isEmpty(appid) || StringUtils.isEmpty(partner)
					|| StringUtils.isEmpty(subAppId)
					|| StringUtils.isEmpty(subPartner)
					|| StringUtils.isEmpty(partnerKey)) {
				res.setCode(-1);
				res.setMessage("商户信息不完整，无法发起支付");
				log.error("公众号api支付，渠道商户信息不完整，无法发起支付，mid="+mid);
				log.warn(result);
				return res;
			}
			// 1.生成预支付回话标识
			prepay_id = getJsApiChannelPrepay_id(appid, partner, partnerKey, subAppId, subPartner, notifyUrl,
					httpRequest.getRemoteAddr(), attach.toJSONString(), order, orderItems);		
			
		} else {// 3直连 （此处：1大商户情形不存在）
			partnerKey = result.getMerchantAppKey();
			appid = result.getMerchantAppId();
			partner = result.getMerchantPartnerId();
			// 交易商户支付信息
			if (StringUtils.isEmpty(appid) || StringUtils.isEmpty(partner) || StringUtils.isEmpty(partnerKey)) {
				res.setCode(-1);
				res.setMessage("商户信息不完整，无法发起支付");
				log.error("公众号api支付，直连商户信息不完整，无法发起支付,mid="+mid);
				log.warn(result);
				return res;
			}
			// 1.生成预支付回话标识
			prepay_id = getJsApiPrepay_id(appid, partner, partnerKey, notifyUrl,
					httpRequest.getRemoteAddr(), attach.toJSONString(), order, orderItems);
		}
		
		if (prepay_id == null) {
			res.setCode(-1);
			res.setMessage("请求异常，支付请求失败");
			log.error("公众号api支付，请求异常，支付请求失败, prepay_id = null");
			return res;
		}	

		// 2.支付参数
		WechatPayment pm = new WechatPayment();
		pm.setPaymentPackage("prepay_id=" + prepay_id);
		pm.sign(appid, partnerKey);

		res.setObj(pm);
		return res;
	}

	/**
	 * （渠道）微信统一下单接口请求，获取微信生成的预支付回话标识prepay_id，用于后续接口调用中使用，该值有效期为2小时
	 * 
	 * @param channelAppId
	 * @param channelPartnerId
	 * @param channelPartnerKey
	 * @param merchantAppId
	 * @param merchantPartnerId
	 * @param notifyUrl
	 * @param clientIP
	 * @param order
	 * @param orderItems
	 * @return
	 */
	public String getJsApiChannelPrepay_id(String channelAppId,
			String channelPartnerId, String channelPartnerKey,
			String merchantAppId, String merchantPartnerId, String notifyUrl,
			String clientIP, String attach, WxOrder order, List<WxOrderDetail> orderItems) {
		ChannelJsSdkPaymentPackage cpkg = new ChannelJsSdkPaymentPackage();
		cpkg.setAppid(channelAppId);
		cpkg.setMch_id(channelPartnerId);
		cpkg.setSub_appid(merchantAppId);
		cpkg.setSub_mch_id(merchantPartnerId);
		cpkg.setNotify_url(notifyUrl);
		cpkg.setSpbill_create_ip(clientIP);
		cpkg.setAttach(attach);
		cpkg.assemble(order, orderItems);
		cpkg.toSign(channelPartnerKey);

		String xmlData = cpkg.getPackageXML();

		String resp = wxHttpClient.postXML(unifiedorder, xmlData);
		String prepay_id = null;
		try {
			Map<String, String> respMap = XMLUtil.doXMLParse(resp);

			prepay_id = respMap.get("prepay_id");
			if (prepay_id == null) {
				log.error(xmlData);
				log.error(resp);
			}
		} catch (JDOMException e) {
			log.error(xmlData);
			log.error(resp);
			log.error(e.getMessage());
			return null;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			log.error(xmlData);
			log.error(resp);
			log.error(e.getMessage());
			return null;
		}

		return prepay_id;
	}

	/**
	 * 微信统一下单接口请求，获取微信生成的预支付回话标识prepay_id，用于后续接口调用中使用，该值有效期为2小时
	 * 
	 * @param partnerId
	 * @param partnerKey
	 * @param notifyUrl
	 * @param clientIP
	 * @param order
	 * @param orderItems
	 * @return
	 */
	public String getJsApiPrepay_id(String appid, String partnerId,
			String partnerKey, String notifyUrl, String clientIP, String attach,
			WxOrder order, List<WxOrderDetail> orderItems) {
		JsSdkPaymentPackage pkg = new JsSdkPaymentPackage();
		pkg.setAppid(appid);
		pkg.setMch_id(partnerId);
		pkg.setNotify_url(notifyUrl);
		pkg.setSpbill_create_ip(clientIP);
		pkg.setAttach(attach);
		pkg.assemble(order, orderItems);
		pkg.toSign(partnerKey);

		String xmlData = pkg.getPackageXML();

		String resp = wxHttpClient.postXML(unifiedorder, xmlData);
		
		String prepay_id = null;
		try {
			Map<String, String> respMap = XMLUtil.doXMLParse(resp);

			prepay_id = respMap.get("prepay_id");
			if (prepay_id == null) {
				log.error(xmlData);
				log.error(resp);
			}
		} catch (JDOMException e) {
			log.error(xmlData);
			log.error(resp);
			log.error(e.getMessage());
			return null;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			log.error(xmlData);
			log.error(resp);
			log.error(e.getMessage());
			return null;
		}

		return prepay_id;
	}
	
	/**
	 * 微信统一下单接口请求，获取微信生成的预支付回话标识prepay_id，用于后续接口调用中使用，该值有效期为2小时
	 * 
	 * @param partnerId
	 * @param partnerKey
	 * @param notifyUrl
	 * @param clientIP
	 * @param order
	 * @param orderItems
	 * @return
	 */
	public String getPrepay_id(String appid, String partnerId,
			String partnerKey, String notifyUrl, String clientIP,
			WxOrder order, List<WxOrderDetail> orderItems) {
		PaymentPackage pkg = new PaymentPackage();
		pkg.setAppid(appid);
		pkg.setMch_id(partnerId);
		pkg.setNotify_url(notifyUrl);
		pkg.setSpbill_create_ip(clientIP);
		pkg.assemble(order, orderItems);
		pkg.toSign(partnerKey);

		String xmlData = pkg.getPackageXML();

		String resp = wxHttpClient.postXML(unifiedorder, xmlData);
		String prepay_id = null;
		try {
			Map<String, String> respMap = XMLUtil.doXMLParse(resp);

			prepay_id = respMap.get("prepay_id");
			
			if (prepay_id == null) {
				log.error(xmlData);
				log.error(resp);
			}
		} catch (JDOMException e) {
			log.error(xmlData);
			log.error(resp);
			log.error(e.getMessage());
			return null;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			log.error(xmlData);
			log.error(resp);
			log.error(e.getMessage());
			return null;
		}

		return prepay_id;
	}

	/**
	 * （渠道）微信统一下单接口请求，获取微信生成的预支付回话标识prepay_id，用于后续接口调用中使用，该值有效期为2小时
	 * 
	 * @param channelAppId
	 * @param channelPartnerId
	 * @param channelPartnerKey
	 * @param merchantAppId
	 * @param merchantPartnerId
	 * @param notifyUrl
	 * @param clientIP
	 * @param order
	 * @param orderItems
	 * @return
	 */
	public String getChannelPrepay_id(String channelAppId,
			String channelPartnerId, String channelPartnerKey,
			String merchantAppId, String merchantPartnerId, String notifyUrl,
			String clientIP, WxOrder order, List<WxOrderDetail> orderItems) {
		ChannelPaymentPackage cpkg = new ChannelPaymentPackage();
		cpkg.setAppid(channelAppId);
		cpkg.setMch_id(channelPartnerId);
		cpkg.setSub_appid(merchantAppId);
		cpkg.setSub_mch_id(merchantPartnerId);
		cpkg.setNotify_url(notifyUrl);
		cpkg.setSpbill_create_ip(clientIP);
		cpkg.assemble(order, orderItems);
		cpkg.toSign(channelPartnerKey);

		String xmlData = cpkg.getPackageXML();

		String resp = wxHttpClient.postXML(unifiedorder, xmlData);
		String prepay_id = null;
		try {
			Map<String, String> respMap = XMLUtil.doXMLParse(resp);

			prepay_id = respMap.get("prepay_id");
			
			if (prepay_id == null) {
				log.error(xmlData);
				log.error(resp);
			}
		} catch (JDOMException e) {
			log.error(xmlData);
			log.error(resp);
			log.error(e.getMessage());
			return null;
		} catch (IOException e) {
			log.error(xmlData);
			log.error(resp);
			log.error(e.getMessage());
			return null;
		}

		return prepay_id;
	}

	/**
	 * v3.3.7版本，微信支付结束的回调操作，对订单修改操作
	 * 
	 * @param request
	 * @param response
	 * @throws Exception
	 */
	@RequestMapping(value = "/recharge-pay-notify", method = RequestMethod.POST)
	public void rechargeAcceptNotification(HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(
				(ServletInputStream) request.getInputStream()));
		String line = null;
		StringBuilder sb = new StringBuilder();
		while ((line = br.readLine()) != null) {
			sb.append(line);
		}
		Map<String, String> resMap = XMLUtil.doXMLParse(sb.toString());
		// 支付结果
		String return_code = resMap.get("return_code");
		String result_code = resMap.get("result_code");
		
		log.error("--------------return_code------------");
		log.error(return_code);
		log.error("--------------result_code------------");
		log.error(result_code);
		
		// 判断签名及结果
		if ("SUCCESS".equals(return_code) && "SUCCESS".equals(result_code)) {
			// 商户订单号
			String orderId = resMap.get("out_trade_no");
			// 金额,以分为单位
			String total_fee = resMap.get("total_fee");
			// 微信支付订单号
			String transactionId = resMap.get("transaction_id");
			// 微信号（用户）
			String openId = resMap.get("sub_openid");
			if (StringUtils.isEmpty(openId)) {
				openId = resMap.get("openid");
			}
			String attach = resMap.get("attach");
			JSONObject attachJson = JSONObject.parseObject(attach);
			String mid = attachJson.getString("mid");
			String operatorId = attachJson.getString("operatorId");
			String shopMid = attachJson.getString("shopMid");
			//请求开放平台微信jssdk支付更新接口
			MemberCardRechargeInput input = new MemberCardRechargeInput();
			input.setOrderId(orderId);
			input.setTotalFee(Integer.valueOf(total_fee));
			input.setTransactionId(transactionId);
			input.setOpenId(openId);
			input.setMid(mid);
			input.setOperatorId(operatorId);
			input.setShopMid(shopMid);
			MemberCardRechargeOutput output = (MemberCardRechargeOutput) wizarposOpenRestClient.post(input, "/wechart/memberRecharge", MemberCardRechargeOutput.class);
			MemberCardRechargeOutput.Result  order = output.getResult();
			
			if ("60034".equals(output.getCode())) {//订单重复支付			
				log.error("Bad request:重复的支付交易-订单[" + orderId + "]已支付");
				sendToCFT(response,
						"<xml><return_code>FAIL</return_code></xml>");
				return;
			}
			if ("0".equals(output.getCode())) {//操作成功
				log.error("--------------WeChart Pay Tran Log------------");
				log.error(orderId);	
				log.error("--------------order.getAmount()------------");
				log.error(order.getAmount());
				log.error("--------------total_fee------------");
				log.error(total_fee);
				log.error("--------------order.getPayStatus()------------");
				log.error(order.getPayStatus());
				log.debug("订单[" + orderId + "]支付成功");
			} else {
				log.error("--------------total_fee------------");
				log.error(total_fee);
				log.warn("订单[" + orderId + "] 错误");
				log.error(output.getMessage());
			}
			log.debug("success 后台通知成功");
			// 给财付通系统发送成功信息，财付通系统收到此结果后不再进行后续通知
		} else {
			log.error("fail 支付失败");
		}
		sendToCFT(response, "<xml><return_code>SUCCESS</return_code></xml>");
		return;
	}
	
	/**
	 * v3.3.7版本，微信支付结束的回调操作，对订单修改操作
	 * 
	 * @param request
	 * @param response
	 * @throws Exception
	 */
	@RequestMapping(value = "/pay-notify", method = RequestMethod.POST)
	public void acceptNotification(HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(
				(ServletInputStream) request.getInputStream()));
		String line = null;
		StringBuilder sb = new StringBuilder();
		while ((line = br.readLine()) != null) {
			sb.append(line);
		}
		Map<String, String> resMap = XMLUtil.doXMLParse(sb.toString());
		// 支付结果
		String return_code = resMap.get("return_code");
		String result_code = resMap.get("result_code");
		
		log.debug("return_code="+return_code);
		log.debug("result_code="+result_code);
		
		// 判断签名及结果
		if ("SUCCESS".equals(return_code) && "SUCCESS".equals(result_code)) {
			// 商户订单号
			String orderId = resMap.get("out_trade_no");
			// 金额,以分为单位
			String total_fee = resMap.get("total_fee");
			// 微信支付订单号
			String transactionId = resMap.get("transaction_id");

			log.debug("out_trade_no="+orderId);
			log.debug("total_fee="+total_fee);
			log.debug("transactionId="+transactionId);
			
			//请求开放平台微信支付更新接口
			WxPayUpdateInput input = new WxPayUpdateInput();
			input.setOrderId(orderId);
			input.setTotalFee(total_fee);
			input.setTransactionId(transactionId);
			WxPayUpdateOutput output = (WxPayUpdateOutput) wizarposOpenRestClient.post(input, "/wxshop/wxPayUpdate", WxPayUpdateOutput.class);
			WxOrder order = output.getWxOrder();
			if ("60020".equals(output.getCode())) {//非法订单号	
				log.error("Invalid order ID [" + orderId + "]");
				sendToCFT(response,
						"<xml><return_code>SUCCESS</return_code></xml>");
				return;
			}	
			
			log.debug("order.getAmount()="+order.getAmount());
			log.debug("order.getPayStatus()="+order.getPayStatus());
			
			if ("60034".equals(output.getCode())) {//订单重复支付			
				log.error("Bad request:重复的支付交易-订单[" + orderId + "]已支付");
				sendToCFT(response,
						"<xml><return_code>FAIL</return_code></xml>");
				return;
			}
			
			if ("0".equals(output.getCode())) {//操作成功
				//更新订单信息，记录交易流水表，已在微信支付更新接口中处理
				log.debug("订单[" + orderId + "]支付成功");
			} else {
				log.error("订单[" + orderId + "] 金额不符");
			}
			log.debug("success 后台通知成功");
			// 给财付通系统发送成功信息，财付通系统收到此结果后不再进行后续通知
		} else {
			log.error("fail 支付失败");
		}
		sendToCFT(response, "<xml><return_code>SUCCESS</return_code></xml>");
		return;
	}

	@RequestMapping(value = "/status", method = RequestMethod.GET)
	@ResponseBody
	public String getOrderStatus(@RequestParam(required = true) String mid,
			@RequestParam(required = true) String orderId,
			HttpServletRequest request) {
		log.debug("-------------------------------check status---");
		log.debug("orderId =" + orderId);
		
		WechartOrderDetailInput input = new WechartOrderDetailInput();
		input.setMid(mid);
		input.setOrderId(orderId);
		WechartOrderOutput output = (WechartOrderOutput) wizarposOpenRestClient.post(input, "/order/detail", WechartOrderOutput.class);

		if (output == null) {//系统错误
			log.error("系统错误，请求开放平台获取订单详情失败，mid="+mid + ", orderId="+orderId);
			return "系统错误，操作失败，请重试";		
		}		
		if ("60020".equals(output.getCode())) {//60020 非法订单号
			log.warn("非法订单号，mid="+mid + ", orderId="+orderId);
			return "非法订单号";	
		}		
		WechartOrder order = output.getResult();		

		log.debug("order.getPayStatus() =" + order.getPayStatus());
		return order.getPayStatus();
	}
	
	private String getBaseURL(HttpServletRequest httpRequest) {
		String url = "http://"
				+ messageSource.getMessage(SysConstants.SERVICE_DNS, null,
						Locale.ENGLISH);
		url += httpRequest.getSession().getServletContext().getContextPath();
		return url;
	}

	/**
	 * 返回处理结果给微信。
	 * 
	 * @param msg
	 *            Success or fail
	 * @throws IOException
	 */
	private void sendToCFT(HttpServletResponse response, String msg) {
		try {
			PrintWriter out = response.getWriter();
			out.println(msg);
			out.flush();
			out.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 餐饮支付页
	 * 
	 * @param orderId
	 * @param mid
	 * @param discount
	 * @param ticketInfoIds
	 * @param httpRequest
	 * @return
	 */
	@RequestMapping(value = "/catering")
	public ModelAndView toCateringPayPage(
			@RequestParam(required = true) String orderId,
			@RequestParam(required = true) String mid,
			@RequestParam(required = false) String discount,
			@RequestParam(required = false) String ticketInfoIds,
			HttpServletRequest httpRequest) {
		String openId = (String) httpRequest.getSession().getAttribute("openId");
		if (StringUtils.isEmpty(openId)) {//session过期，跳转到登录注册页面
			return new ModelAndView("forward:/vip/login");	
		}
		
		return new ModelAndView("catering_tenpay").addObject("openId", openId)
				.addObject("mid", mid).addObject("orderId", orderId)
				.addObject("discount", discount)
				.addObject("ticketInfoIds", ticketInfoIds);
	}
	
	// 微信支付v3.3.7版本
	@RequestMapping(value = "/catering/message", method = RequestMethod.GET)
	@ResponseBody
	public RespMessage getTakeoutPaymentMessage(String openId,
			@RequestParam(required = true) String orderId,
			@RequestParam(required = true) String discount,// 本次交易中代金券总面值，单位元
			@RequestParam(value = "ticketInfoIds") String ticketInfoIds,
			HttpServletRequest request) {
		RespMessage res = new RespMessage();
		String cardId = (String) request.getSession().getAttribute("mrtCardId");
		if (StringUtils.isEmpty(cardId)) {//session过期，跳转到登录注册页面
			res.setCode(-1);
			res.setMessage("session过期，请重新登录");
			log.error("session过期， orderId=" + orderId);
			return res;				
		}
		
		WxSaleOrderQueryInput wxSaleOrderQueryInput = new WxSaleOrderQueryInput();
		wxSaleOrderQueryInput.setOrderId(orderId);
		WxSaleOrderQueryV1_1Output output = (WxSaleOrderQueryV1_1Output) wizarposOpenRestClient.postV1_1(wxSaleOrderQueryInput, "/wx_shop/wxSaleOrderQuery", WxSaleOrderQueryV1_1Output.class);
		
		if (output == null) {//系统错误	
			res.setCode(-1);
			res.setMessage("系统错误，操作失败，请重试");
			log.error("请求开放平台获取餐饮订单信息失败， orderId=" + orderId);
			return res;			
		}		
		if ("60020".equals(output.getCode())) {//60020 非法订单号
			res.setCode(-1);
			res.setMessage("无效支付请求：非法订单号");
			log.warn("无效支付请求，非法订单号："+ orderId);
			return res;
		}
		WxSaleOrderQueryV1_1Output.Result resultTmp = output.getResult();
		String mid = resultTmp.getMid();
		String shopMid = resultTmp.getShopMid();
		SaleOrder order = new SaleOrder();
		order.setId(resultTmp.getId());
		order.setMid(shopMid);
		order.setFid(mid);
		order.setOrderNo(resultTmp.getOrderNo());
		order.setAmount(resultTmp.getAmount());

		Integer amount = order.getAmount();
		Integer inputAmount = 0;
		// 本次交易中代金券总面值，单位元
		// 若本次微信支付中用券则减去代金券面值
		Integer extraAmount = 0; // 折扣金额
		if (!StringUtils.isEmpty(discount)) {
			extraAmount = Utils.Amount2DB(discount);
			amount = order.getAmount() - extraAmount;
			
			order.setAmount(order.getAmount() - extraAmount);
		} 
		inputAmount = amount + extraAmount;
		// 附加数据(支付成功后会返回)
		JSONObject attach = new JSONObject();
		attach.put("extraAmount", extraAmount);
		attach.put("orderNo", order.getOrderNo());

		GetOrderNoOutput.Result rst = wPosHttpClient.getOrderNo(mid, shopMid, SysConstants.WX, cardId, amount, extraAmount, inputAmount, ticketInfoIds, "");
		if (rst == null) {
			res.setCode(-1);
			res.setMessage("系统错误，操作失败，请重试");
			log.error("请求开放平台获取订单编号失败， orderId=" + orderId);
			return res;			
		}
		String orderNo = rst.getOrderNo();
		// 重新设置签名用订单编号
		order.setOrderNo(orderNo);
		String tranLogId = rst.getTranLogId();
		attach.put("tranLogId", tranLogId);
		
		List<SaleOrderItem> orderItems = new ArrayList<SaleOrderItem>();
		List<SaleOrderItemView> saleOrderItemView = resultTmp.getSaleOrderItems();
		if (saleOrderItemView != null) {
			for (SaleOrderItemView itemView : saleOrderItemView)	{
				SaleOrderItem it = new SaleOrderItem();
				try {
					BeanUtils.copyProperties(it, itemView);
				} catch (IllegalAccessException | InvocationTargetException e) {
					//do nothing
				}
				orderItems.add(it);
			}
		}		

		// 回调地址
		String notifyUrl = getBaseURL(request) + "/tenpay/catering-pay-notify";
		log.debug(notifyUrl);

		// 请求开放平台获取商户支付信息数据
		MerchantDefPayInfoInput merchantDefPayInfoInput = new MerchantDefPayInfoInput();
		merchantDefPayInfoInput.setMid(mid);
		merchantDefPayInfoInput.setPayType("1");
		MerchantDefPayInfoOutput merchantDefPayInfoOutput = (MerchantDefPayInfoOutput) wizarposOpenRestClient.post(merchantDefPayInfoInput, "/merchantdef/payInfo", MerchantDefPayInfoOutput.class);				
		if (merchantDefPayInfoOutput == null
				|| !"0".equals(merchantDefPayInfoOutput.getCode())
				|| merchantDefPayInfoOutput.getResult() == null) {
			res.setCode(-1);
			res.setMessage("系统异常，无法发起支付");
			log.error("请求开发平台获取餐饮商户支付信息失败，无法发起支付");
			log.error(merchantDefPayInfoOutput.getMessage());
			return res;
		}	
		Result result = merchantDefPayInfoOutput.getResult();
		// 微信商户平台ID
		String partner = null;
		// 微信商户平台支付密钥
		String partnerKey = null;
		// 微信公众平台应用ID
		String appid = null;
		// 预支付回话标识
		String prepay_id = null;
		
		// 连接类型1.大商户2渠道3直连
		if ("2".equals(result.getConnectionType())) {// 2渠道
			partnerKey = result.getChannelAppKey();
			appid = result.getChannelAppId();
			partner = result.getChannelPartnerId();
			// 子商户公众账号ID
			String subAppId = result.getMerchantAppId();
			// 子商户号
			String subPartner = result.getMerchantPartnerId();
			// 交易商户支付信息
			if (StringUtils.isEmpty(appid) || StringUtils.isEmpty(partner)
					|| StringUtils.isEmpty(subAppId)
					|| StringUtils.isEmpty(subPartner)
					|| StringUtils.isEmpty(partnerKey)) {
				res.setCode(-1);
				res.setMessage("商户信息不完整，无法发起支付");
				log.error("餐饮渠道商户信息不完整，无法发起支付，mid="+mid);
				log.error(result);
				return res;
			}
			// 1.生成预支付回话标识
			prepay_id = getTakeoutChannelPrepay_id(appid, partner, partnerKey, subAppId, subPartner, notifyUrl,
					request.getRemoteAddr(), attach.toJSONString(), order, orderItems, openId);		
			
		} else {// 3直连 （此处：1大商户情形不存在）
			partnerKey = result.getMerchantAppKey();
			appid = result.getMerchantAppId();
			partner = result.getMerchantPartnerId();
			// 交易商户支付信息
			if (StringUtils.isEmpty(appid) || StringUtils.isEmpty(partner) || StringUtils.isEmpty(partnerKey)) {
				res.setCode(-1);
				res.setMessage("商户信息不完整，无法发起支付");
				log.error("餐饮直连商户信息不完整，无法发起支付,mid="+mid);
				log.error(result);
				return res;
			}
			// 1.生成预支付回话标识
			prepay_id = getTakeoutPrepay_id(appid, partner, partnerKey,
					notifyUrl, request.getRemoteAddr(), attach.toJSONString(), order, orderItems, openId);
		}

		if (prepay_id == null) {
			res.setCode(-1);
			res.setMessage("请求异常，支付请求失败");
			log.error("请求异常，支付请求失败, prepay_id = null");
			return res;
		}

		// 2.支付参数
		WechatPayment pm = new WechatPayment();
		pm.setPaymentPackage("prepay_id=" + prepay_id);
		pm.sign(appid, partnerKey);

		res.setObj(pm);
		return res;
	}
	
	/**
	 * （渠道）微信统一下单接口请求，获取微信生成的预支付回话标识prepay_id，用于后续接口调用中使用，该值有效期为2小时
	 * 
	 * @param channelAppId
	 * @param channelPartnerId
	 * @param channelPartnerKey
	 * @param merchantAppId
	 * @param merchantPartnerId
	 * @param notifyUrl
	 * @param clientIP
	 * @param order
	 * @param orderItems
	 * @return
	 */
	public String getTakeoutChannelPrepay_id(String channelAppId,
			String channelPartnerId, String channelPartnerKey,
			String merchantAppId, String merchantPartnerId, String notifyUrl,
			String clientIP, String attach, SaleOrder order, List<SaleOrderItem> orderItems, String openId) {

		StringBuffer _body = new StringBuffer();
		for (SaleOrderItem item : orderItems) {
			if (item == null) {
				continue;
			}
			MerchandiseSyncInput producthotInput = new MerchandiseSyncInput();
			producthotInput.setMid(order.getMid());
			producthotInput.setProductId(item.getProductId());
			MerchandiseSyncOutput producthotOutput = (MerchandiseSyncOutput) wizarposOpenRestClient.post(producthotInput,"/product/merchandise",MerchandiseSyncOutput.class);
			List<Merchandise> products = producthotOutput.getResult();
			Merchandise pro = products.get(0);		
			if (pro != null && pro.getName() != null
					&& !pro.getName().isEmpty()) {
				_body.append(pro.getName());
				if (orderItems.size() > 1) {
					_body.append("...");
				}
				break;
			}
		}
		ChannelPaymentPackage cpkg = new ChannelPaymentPackage();
		cpkg.setAppid(channelAppId);
		cpkg.setMch_id(channelPartnerId);
		cpkg.setSub_appid(merchantAppId);
		cpkg.setSub_mch_id(merchantPartnerId);
		cpkg.setNotify_url(notifyUrl);
		cpkg.setSpbill_create_ip(clientIP);
		cpkg.setAttach(attach);
		cpkg.assemble(order, _body.toString(), openId);
		cpkg.toSign(channelPartnerKey);
		
		String xmlData = cpkg.getPackageXML();

		String resp = wxHttpClient.postXML(unifiedorder, xmlData);
		String prepay_id = null;
		try {
			Map<String, String> respMap = XMLUtil.doXMLParse(resp);
			if (respMap != null) {
				prepay_id = respMap.get("prepay_id");
			}
			if (prepay_id == null) {
				log.error(xmlData);
				log.error(resp);
			}
		} catch (Exception e) {
			log.error(xmlData);
			log.error(resp);
			log.error(e.getMessage());
			return null;
		}

		return prepay_id;
	}
	
	/**
	 * 微信统一下单接口请求，获取微信生成的预支付回话标识prepay_id，用于后续接口调用中使用，该值有效期为2小时
	 * 
	 * @param partnerId
	 * @param partnerKey
	 * @param notifyUrl
	 * @param clientIP
	 * @param order
	 * @param orderItems
	 * @return
	 */
	public String getTakeoutPrepay_id(String appid, String partnerId,
			String partnerKey, String notifyUrl, String clientIP, String attach,
			SaleOrder order, List<SaleOrderItem> orderItems, String openId) {

		StringBuffer _body = new StringBuffer();
		for (SaleOrderItem item : orderItems) {
			if (item == null) {
				continue;
			}
			MerchandiseSyncInput producthotInput = new MerchandiseSyncInput();
			producthotInput.setMid(order.getMid());
			producthotInput.setProductId(item.getProductId());
			MerchandiseSyncOutput producthotOutput = (MerchandiseSyncOutput) wizarposOpenRestClient.post(producthotInput,"/product/merchandise",MerchandiseSyncOutput.class);
			List<Merchandise> products = producthotOutput.getResult();
			Merchandise pro = products.get(0);		
			if (pro != null && pro.getName() != null
					&& !pro.getName().isEmpty()) {
				_body.append(pro.getName());
				if (orderItems.size() > 1) {
					_body.append("...");
				}
				break;
			}
		}

		PaymentPackage pkg = new PaymentPackage();
		pkg.setAppid(appid);
		pkg.setMch_id(partnerId);
		pkg.setNotify_url(notifyUrl);
		pkg.setSpbill_create_ip(clientIP);
		pkg.setAttach(attach);
		pkg.assemble(order, _body.toString(), openId);
		pkg.toSign(partnerKey);

		String xmlData = pkg.getPackageXML();

		String resp = wxHttpClient.postXML(unifiedorder, xmlData);
		String prepay_id = null;
		try {
			Map<String, String> respMap = XMLUtil.doXMLParse(resp);
			if (respMap != null) {
				prepay_id = respMap.get("prepay_id");
			}
			if (prepay_id == null) {
				log.error(xmlData);
				log.error(resp);
			}
		} catch (Exception e) {
			log.error(xmlData);
			log.error(resp);
			log.error(e.getMessage());
			return null;
		}

		return prepay_id;
	}
	
	/**
	 * v3.3.7版本，微信支付结束的回调操作，对订单修改操作
	 * 
	 * @param request
	 * @param response
	 * @throws Exception
	 */
	@RequestMapping(value = "/catering-pay-notify", method = RequestMethod.POST)
	public void takeoutAcceptNotification(HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(
				(ServletInputStream) request.getInputStream()));
		String line = null;
		StringBuilder sb = new StringBuilder();
		while ((line = br.readLine()) != null) {
			sb.append(line);
		}
		Map<String, String> resMap = XMLUtil.doXMLParse(sb.toString());
		// 支付结果
		String return_code = resMap.get("return_code");
		String result_code = resMap.get("result_code");

		log.debug("return_code="+return_code);
		log.debug("result_code="+result_code);
		
		// 判断签名及结果
		if ("SUCCESS".equals(return_code) && "SUCCESS".equals(result_code)) {
			// 商户支付用订单号
			String payOrderNo = resMap.get("out_trade_no");
			// 金额,以分为单位
			String total_fee = resMap.get("total_fee");
			// 微信支付订单号
			String transactionId = resMap.get("transaction_id");
			
			// 微信号（用户）
			String openId = resMap.get("sub_openid");
			if (StringUtils.isEmpty(openId)) {
				openId = resMap.get("openid");
			}
			
			String attach = resMap.get("attach");
			JSONObject attachJson = JSONObject.parseObject(attach);
			// 商户订单号
			String orderNo = attachJson.getString("orderNo");
			String masterTranLogId = attachJson.getString("tranLogId");
			Integer extraAmount = attachJson.getInteger("extraAmount");
			
			WxSaleOrderQueryInput wxSaleOrderQueryInput = new WxSaleOrderQueryInput();
			wxSaleOrderQueryInput.setOrderNo(orderNo);
			WxSaleOrderQueryV1_1Output output = (WxSaleOrderQueryV1_1Output) wizarposOpenRestClient.postV1_1(wxSaleOrderQueryInput, "/wx_shop/wxSaleOrderQuery", WxSaleOrderQueryV1_1Output.class);				
	
			if ("60020".equals(output.getCode())) {//60020 非法订单号
				log.error("Invalid order No [" + orderNo + "]");
				sendToCFT(response,
						"<xml><return_code>SUCCESS</return_code></xml>");
				return;
			}			
			WxSaleOrderQueryV1_1Output.Result order = output.getResult();
			if ("1".equals(order.getPayStatus())) {
				log.error("Bad request:重复的支付交易:订单[" + orderNo + "]已支付");
				sendToCFT(response,
						"<xml><return_code>FAIL</return_code></xml>");
				return;
			}
			String mid = order.getMid();
			CateringSaleOrderPayInput cateringSaleOrderPayInput = new CateringSaleOrderPayInput();
			cateringSaleOrderPayInput.setMid(mid);
			cateringSaleOrderPayInput.setOrderId(order.getId());
			cateringSaleOrderPayInput.setAmount(Integer.valueOf(total_fee));
			cateringSaleOrderPayInput.setPrint(true);
			cateringSaleOrderPayInput.setTransactionId(transactionId);
			cateringSaleOrderPayInput.setOpenId(openId);
			cateringSaleOrderPayInput.setPayOrderNo(payOrderNo);
			cateringSaleOrderPayInput.setMasterTranLogId(masterTranLogId);
			List<Payment> payments = new ArrayList<Payment>();
			Payment payment = new Payment();
			payment.setPayType("5"); // 微信支付
			payment.setAmount(total_fee);
			payments.add(payment);
			cateringSaleOrderPayInput.setPayments(payments);
			cateringSaleOrderPayInput.setExtraAmount(extraAmount);

			CateringSaleOrderPayOutput cateringSaleOrderPayOutput = (CateringSaleOrderPayOutput) wizarposOpenRestClient.post(cateringSaleOrderPayInput, "/catering/pay", CateringSaleOrderPayOutput.class);				
			if (cateringSaleOrderPayOutput == null) {
				log.error("订单[" + orderNo + "]支付成功，呼叫开放平台结算失败。");
			} else if ("0".equals(cateringSaleOrderPayOutput.getCode())) {
				log.debug("订单[" + orderNo + "]支付成功， 记录成功");
				CateringSaleOrderPayOutput.Result result = cateringSaleOrderPayOutput.getResult();
				if (result != null) {
					// 推送管家号收款模板消息
					wPosHttpClient.pushHouseKeeperPaymentMsg(mid, result.getTranLogId());
				}
			} else {
				log.debug("订单[" + orderNo + "]错误，" + cateringSaleOrderPayOutput.getCode());
				log.debug("订单[" + orderNo + "]错误，" + cateringSaleOrderPayOutput.getMessage());
			}
			
			log.debug("success 后台通知成功");
			// 给财付通系统发送成功信息，财付通系统收到此结果后不再进行后续通知
		} else {
			log.debug("fail 支付失败");
		}
		sendToCFT(response, "<xml><return_code>SUCCESS</return_code></xml>");
		return;
	}
	
	/**
	 * 使用代金券
	 * 
	 * @param mid
	 * @param openId
	 * @param orderId
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	@RequestMapping(value = "/ticket_use")
	public @ResponseBody RespMessage ticketUse(
			@RequestParam(value = "orderId", required = true) String orderId,
			@RequestParam(value = "mid", required = true) String mid,
			@RequestParam(value = "ticketInfoIds", required = true) String ticketInfoIds,
			HttpServletRequest httpRequest) {
		RespMessage res = new RespMessage();	
		
		String openId = (String) httpRequest.getSession().getAttribute("openId");
		if (StringUtils.isEmpty(openId)) {//session过期，跳转到登录注册页面
			res.setCode(1);
			res.setMessage("核销券出错，请联系商家");
			return res;	
		}
		// 请求开放平台券使用接口
		TicketUseInput input = new TicketUseInput();
		input.setMid(mid);
		input.setOrderId(orderId);
		input.setOpenId(openId);
		input.setTicketInfoId(Arrays.asList(ticketInfoIds.split(",")));
		
		TicketUseOutput output = (TicketUseOutput) wizarposOpenRestClient.post(input, "/catering/ticket/use", TicketUseOutput.class);		

		if (output != null && SysConstants.OPEN_SUCCESS.equals(output.getCode())) {
			res.setCode(0);
		} else if (output == null) {
			log.error("请求开放平台券使用接口失败");
			log.error("微信支付核销券失败，orderId:" + orderId);
			res.setCode(1);
			res.setMessage("核销券出错，请联系商家");
		} else {
			log.error("请求开放平台券使用接口失败");
			log.error("微信支付核销券失败，orderId:" + orderId);
			log.error("code:" + output.getCode());
			log.error("message:" + output.getMessage());
			res.setCode(1);
			res.setMessage("核销券出错，请联系商家");
		}		
		return res;
	}	
	
	@RequestMapping(value = "/catering/status", method = RequestMethod.GET)
	@ResponseBody
	public String getSaleOrderStatus(
			@RequestParam(required = true) String orderId,
			HttpServletRequest request) {
		log.debug("check canyin order status");
		log.debug("orderId =" + orderId);
		WxSaleOrderQueryInput wxSaleOrderQueryInput = new WxSaleOrderQueryInput();
		wxSaleOrderQueryInput.setOrderId(orderId);
		WxSaleOrderQueryOutput output = (WxSaleOrderQueryOutput) wizarposOpenRestClient.post(wxSaleOrderQueryInput, "/wxshop/wxSaleOrderQuery", WxSaleOrderQueryOutput.class);				
		if (output == null) {//系统错误	
			log.error("系统错误，请求开放平台获取订单详情失败，orderId="+orderId);
			return "系统错误，操作失败，请重试";	
		}		
		if ("60020".equals(output.getCode())) {//60020 非法订单号
			log.error("系统错误，请求开放平台获取订单详情失败，orderId="+orderId);
			return "非法订单号";
		}			
		
		log.debug("order.getPayStatus() =" + output.getResult().getPayStatus());
		return output.getResult().getPayStatus();
	}

	/**
	 * 判断订单是否存在（若订单已存在，则说明已支付完成）
	 * 
	 * @param mid
	 * @param orderId
	 * @param request
	 * @return
	 */
	@RequestMapping(value = "/jsapi-status", method = RequestMethod.GET)
	@ResponseBody
	public String getJsApiOrderStatus(@RequestParam(required = true) String mid,
			@RequestParam(required = true) String orderId,
			HttpServletRequest request) {
		log.debug("jsapi check status, orderId =" + orderId);
		
		WechartOrderDetailInput input = new WechartOrderDetailInput();
		input.setMid(mid);
		input.setOrderId(orderId);
		WechartOrderOutput output = (WechartOrderOutput) wizarposOpenRestClient.post(input, "/order/detail", WechartOrderOutput.class);
		if (output == null) {//系统错误
			log.warn("系统错误，操作失败，请重试");
			return "系统错误，操作失败，请重试";		
		}		
		// 订单不存在，支付未完成
		if ("60020".equals(output.getCode())) {//60020 非法订单号
			return "0";	
		}		
		WechartOrder order = output.getResult();		
		log.debug("order.getPayStatus() =" + order.getPayStatus());
		return order.getPayStatus();
	}

	/**
	 * 确认支付页面（会员卡充值用）
	 * 
	 * @param id
	 * @param httpRequest
	 * @return
	 */
	@RequestMapping(value = "/recharge")
	public ModelAndView recharge(@RequestParam(required = true) String mid,
			HttpServletRequest httpRequest) {
		Map<String, Object> data = new HashMap<String, Object>();
		String isMainStore = "0";
		String shopMid = mid;
		// 查询商户信息
		MerchantDefQueryInput merchantDefQueryInput = new MerchantDefQueryInput();
		merchantDefQueryInput.setMid(mid);
		MerchantDefQueryOutput merchantDefouptut = (MerchantDefQueryOutput) wizarposOpenRestClient.post(merchantDefQueryInput, "/merchantdef/info", MerchantDefQueryOutput.class);
		if (merchantDefouptut == null) {
			log.error("请求开放平台查询商户信息接口失败");
			log.error("mid="+mid);
			data.put("err_message", "系统错误");
			return new ModelAndView("error", data);
		} else if (SysConstants.OPEN_SUCCESS.equals(merchantDefouptut.getCode())) {
			MerchantDef merchantDef =  merchantDefouptut.getResult();
			data.put("merchant", merchantDef);
			data.put("mid", mid);
			isMainStore = merchantDef.getIsVirtualMainStore();
			data.put("isMainStore", isMainStore);
		} else {
			log.error("code：" + merchantDefouptut.getCode());
			log.error("message：" + merchantDefouptut.getMessage());
			log.error("mid="+mid);
			data.put("err_message", "系统异常");
			return new ModelAndView("error", data);
		}
		// 获取商家信息
		GetShopInfoInput getShopInfoInput = new GetShopInfoInput();
		getShopInfoInput.setMid(mid);
		getShopInfoInput.setType("0"); // 0 充值
		GetShopInfoOutput getShopInfoOutput = (GetShopInfoOutput) wizarposOpenRestClient.post(getShopInfoInput, "/dzx/getShopInfo", GetShopInfoOutput.class);								
		if (getShopInfoOutput == null) {
			log.error("请求开放平台获取商户信息（fid下mid集合）或门店信息（mid下sn集合）接口失败");
			log.error("mid："+mid);
			return new ModelAndView("error").addObject("err_message", SysConstants.OPEN_ERROR_MESSAGE);			
		} else if (!SysConstants.OPEN_SUCCESS.equals(getShopInfoOutput.getCode())) {
			log.error("code：" + getShopInfoOutput.getCode());
			log.error("message：" + getShopInfoOutput.getMessage());
			return new ModelAndView("error").addObject("err_message", getShopInfoOutput.getMessage());	
		}
		List<ShopInfoPair> shopInfoPairs = getShopInfoOutput.getResult().getShopInfoPairs();
		data.put("shopInfoPairs", shopInfoPairs);
		if ("1".equals(isMainStore) && shopInfoPairs != null && shopInfoPairs.size() < 2) {
			shopMid = shopInfoPairs.get(0).getMid();
		}
		// 获取员工编号信息（东志信用）
		GetEmployeeslInput getEmployeeslInput = new GetEmployeeslInput();
		getEmployeeslInput.setMid(shopMid);
		GetEmployeesOutput getEmployeesOutput = (GetEmployeesOutput) wizarposOpenRestClient.post(getEmployeeslInput, "/dzx/get/employees", GetEmployeesOutput.class);
		if (getEmployeesOutput == null) {
			log.error("请求开放平台获取员工编号信息接口失败");
			log.error("mid="+mid);
			data.put("err_message", "系统错误");
			return new ModelAndView("error", data);
		} else if (SysConstants.OPEN_SUCCESS.equals(getEmployeesOutput.getCode())) {
			data.put("employeePairs", getEmployeesOutput.getResult().getEmployeePairs());
		} else {
			log.error("code：" + getEmployeesOutput.getCode());
			log.error("message：" + getEmployeesOutput.getMessage());
			log.error("mid="+mid);
			data.put("err_message", "系统异常");
			return new ModelAndView("error", data);
		}
		
		//请求微信 js api的完整页面地址
		StringBuffer tmpUrl = new StringBuffer();
		tmpUrl.append(getBaseURL(httpRequest));
		tmpUrl.append("/tenpay/recharge");
		tmpUrl.append("?mid=");
		tmpUrl.append(mid);
		String url = tmpUrl.toString();
		// 请求微信api的ticket
		AccessTokenInput jsInput = new AccessTokenInput();
		jsInput.setMid(mid);
		
		CommonResultOutput jsOutput = (CommonResultOutput) wizarposOpenRestClient.post(jsInput, "/weixin/ticket/js/forpay", CommonResultOutput.class);		

		if (jsOutput == null) {// 请求开放平台出错
			log.error("mid="+mid);
			log.error("请求开放平台获取支付用js ticket接口失败");
			return new ModelAndView("error").addObject("err_message",
					SysConstants.OPEN_ERROR_MESSAGE);
		} else if (SysConstants.NO_MERCHANT_CODE.equals(jsOutput.getCode())) {// 非法的慧商户号
			log.error("mid="+mid);
			return new ModelAndView("error").addObject("err_message",
					SysConstants.NO_MERCHANT);
		} else if (!SysConstants.OPEN_SUCCESS.equals(jsOutput.getCode())) {
			log.error("mid="+mid);
			log.error("code："+jsOutput.getCode());
			log.error("message："+jsOutput.getMessage());
			return new ModelAndView("error").addObject("err_message",
					SysConstants.OPEN_ERROR_MESSAGE);
		}
		
		Map<String, String> jsresult = (Map<String, String>) jsOutput.getResult();
		String jsTicket = jsresult.get("ticket");
		String appId = jsresult.get("appid");
		//微信 js config 签名
		Map<String, String> ret = WxSignUtil.commonSign(jsTicket, url,
				WxSignUtil.createTimestamp());
		
		data.put("appId", appId);
		
		Map<String, Object> resMap = wPosHttpClient.getRechargeMarketActivity(mid);
		if (resMap == null) {
			return new ModelAndView("error").addObject("err_message", SysConstants.OPEN_ERROR_MESSAGE);
		}
		List<RechargeMarketInfo> rechargeMarketInfos = (List<RechargeMarketInfo>) resMap.get("rechargeMarketInfos");
		data.put("rechargeMarketInfos", rechargeMarketInfos);
		
		return new ModelAndView("card/recharge", data).addAllObjects(ret);
	}
	
	/**
	 * 获取微信订单编号
	 * 
	 * @param mid
	 * @param orderId
	 * @param request
	 * @return
	 */
	@RequestMapping(value = "/getWxOrderNo", method = RequestMethod.GET)
	@ResponseBody
	public String getWxOrderNo(@RequestParam(required = true) String mid,
			HttpServletRequest request) {
		// 生成微信订单编号
		String wxOrderNo = wPosHttpClient.getWxOrderNo(mid);
		if (StringUtils.isEmpty(wxOrderNo)) {
			return "-1";
		}
		return wxOrderNo;
	}
	
	
	/**
	 * 进入公众号支付页
	 * 
	 * @param mid
	 * @param sn
	 * @param orderId
	 * @param httpRequest
	 * @return
	 */
	@RequestMapping(value = "/pay_wx")
	public ModelAndView payTmp(@RequestParam(required = true) String mid,
			@RequestParam(required = true) String sn,
			@RequestParam(required = false) String orderId,
			HttpServletRequest httpRequest) {
		Map<String, Object> data = new HashMap<String, Object>();
		// 查询商户信息
		MerchantDefQueryInput merchantDefQueryInput = new MerchantDefQueryInput();
		merchantDefQueryInput.setMid(mid);
		MerchantDefQueryOutput merchantDefouptut = (MerchantDefQueryOutput) wizarposOpenRestClient.post(merchantDefQueryInput, "/merchantdef/info", MerchantDefQueryOutput.class);
		if (merchantDefouptut == null) {
			log.error("请求开放平台查询商户信息接口失败");
			log.error("mid="+mid);
			log.error("sn="+sn);
			log.error("orderId="+orderId);
			data.put("err_message", "系统错误");
			return new ModelAndView("error", data);
		} else if (SysConstants.OPEN_SUCCESS.equals(merchantDefouptut.getCode())) {
			data.put("merchant", merchantDefouptut.getResult());
			data.put("mid", mid);
			data.put("sn", sn);
		} else {
			log.error("code：" + merchantDefouptut.getCode());
			log.error("message：" + merchantDefouptut.getMessage());
			log.error("mid="+mid);
			log.error("sn="+sn);
			log.error("orderId="+orderId);
			data.put("err_message", "系统异常");
			return new ModelAndView("error", data);
		}
		WechartOrderDetailInput orderDetailInput = new WechartOrderDetailInput();
		orderDetailInput.setMid(mid);
		orderDetailInput.setOrderId(orderId);
		WechartOrderOutput orderOutput = (WechartOrderOutput) wizarposOpenRestClient.post(orderDetailInput, "/order/detail", WechartOrderOutput.class);
		if (orderOutput == null) {
			log.error("请求开放平台查询微信订单详情失败");
			log.error("mid="+mid);
			log.error("sn="+sn);
			log.error("orderId="+orderId);
			data.put("err_message", "系统错误");
		} else if (SysConstants.OPEN_SUCCESS.equals(orderOutput.getCode())) {
			data.put("orderId", orderId);
			String amount = orderOutput.getResult().getAmount();
			data.put("amount", amount);
			data.put("showAmount", Utils.formatAmount(amount));
		} else {
			
		}
		//请求微信 js api的完整页面地址
		String url = httpRequest.getScheme()+"://"; // 请求协议 http 或 https 
		url+=httpRequest.getServerName(); // 请求服务器 
		url+=httpRequest.getRequestURI(); // 工程名+方法名
		if(httpRequest.getQueryString()!=null) { //判断请求参数是否为空
			url+="?"+httpRequest.getQueryString(); // 参数
		}
		// 请求微信api的ticket
		AccessTokenInput jsInput = new AccessTokenInput();
		jsInput.setMid(mid);
		
		CommonResultOutput jsOutput = (CommonResultOutput) wizarposOpenRestClient.post(jsInput, "/weixin/ticket/js/forpay", CommonResultOutput.class);		
		if (jsOutput == null) {// 请求开放平台出错
			log.error("mid="+mid);
			log.error("请求开放平台获取支付用js ticket接口失败");
			return new ModelAndView("error").addObject("err_message",
					SysConstants.OPEN_ERROR_MESSAGE);
		} else if (SysConstants.NO_MERCHANT_CODE.equals(jsOutput.getCode())) {// 非法的慧商户号
			log.error("mid="+mid);
			return new ModelAndView("error").addObject("err_message",
					SysConstants.NO_MERCHANT);
		} else if (!SysConstants.OPEN_SUCCESS.equals(jsOutput.getCode())) {
			log.error("mid="+mid);
			log.error("code："+jsOutput.getCode());
			log.error("message："+jsOutput.getMessage());
			return new ModelAndView("error").addObject("err_message",
					SysConstants.OPEN_ERROR_MESSAGE);
		}
		
		Map<String, String> jsresult = (Map<String, String>) jsOutput.getResult();
		String jsTicket = jsresult.get("ticket");
		String appId = jsresult.get("appid");
		//微信 js config 签名
		Map<String, String> ret = WxSignUtil.commonSign(jsTicket, url,
				WxSignUtil.createTimestamp());
		
		data.put("appId", appId);
		
		return new ModelAndView("jsapipay/pay_wx", data).addAllObjects(ret);
	}
	/**
	 * 公众号支付统一下单
	 * 
	 * @param mid
	 * @param sn
	 * @param orderId
	 * @param amount
	 * @param httpRequest
	 * @return
	 */
	@RequestMapping(value = "/app_message")
	@ResponseBody
	public RespMessage getAppMessage(
			@RequestParam(required = true) String mid,
			@RequestParam(required = true) String sn,
			@RequestParam(required = false) String orderId,
			@RequestParam(required = true) String amount,
			HttpServletRequest httpRequest) {
		RespMessage res = new RespMessage();
		String openId = (String) httpRequest.getSession().getAttribute("openId");
		if (StringUtils.isEmpty(openId)) {//session过期
			res.setCode(-2);
			res.setMessage("session过期，请重新扫码完成支付");
			return res;	
		}
		// 校验金额
		int tranAmount = 0;
		try {
			tranAmount = Integer.valueOf(amount);
		} catch (NumberFormatException e) {
			e.printStackTrace();
			res.setCode(-1);
			res.setMessage("金额非法：" + amount);
			log.warn("金额非法：" + amount);
			return res;		
		}

		// 查询商户信息
		MerchantDefQueryInput merchantDefQueryInput = new MerchantDefQueryInput();
		merchantDefQueryInput.setMid(mid);
		MerchantDefQueryOutput merchantDefouptut = (MerchantDefQueryOutput) wizarposOpenRestClient.post(merchantDefQueryInput, "/merchantdef/info", MerchantDefQueryOutput.class);
		WxOrderDetail wxOrderDetail = new WxOrderDetail();
		if (merchantDefouptut != null && SysConstants.OPEN_SUCCESS.equals(merchantDefouptut.getCode())) {
			wxOrderDetail.setProductName(merchantDefouptut.getResult().getMerchantName());
		} else {
			wxOrderDetail.setProductName("慧银微信支付");
		}
		List<WxOrderDetail> orderItems = new ArrayList<WxOrderDetail>();
		orderItems.add(wxOrderDetail);
		
		// 回调地址
		StringBuffer baseUrl = new StringBuffer(httpRequest.getScheme()).append("://") // 请求协议 http 或 https 
		.append(httpRequest.getServerName()) // 请求服务器 
		.append(httpRequest.getRequestURI().replace(httpRequest.getServletPath(), "")); // 工程名
		String notifyUrl = baseUrl.append("/tenpay/app_pay_notify").toString();
		log.debug(notifyUrl);

		// 请求开放平台获取商户支付信息数据
		MerchantDefPayInfoInput merchantDefPayInfoInput = new MerchantDefPayInfoInput();
		merchantDefPayInfoInput.setMid(mid);
		merchantDefPayInfoInput.setPayType("1");
		MerchantDefPayInfoOutput merchantDefPayInfoOutput = (MerchantDefPayInfoOutput) wizarposOpenRestClient.post(merchantDefPayInfoInput, "/merchantdef/payInfo", MerchantDefPayInfoOutput.class);				
		if (merchantDefPayInfoOutput == null
				|| !"0".equals(merchantDefPayInfoOutput.getCode())
				|| merchantDefPayInfoOutput.getResult() == null) {
			log.warn(merchantDefPayInfoOutput.getMessage());
			res.setCode(-1);
			res.setMessage("系统异常，无法发起支付");
			log.error("请求开发平台获取商户支付信息失败，无法发起支付");
			return res;
		}	
		Result result = merchantDefPayInfoOutput.getResult();
		// 微信商户平台ID
		String partner = null;
		// 微信商户平台支付密钥
		String partnerKey = null;
		// 微信公众平台应用ID
		String appid = null;
		// 预支付回话标识
		String prepay_id = null;
		
		// 第三方订单号
		GetOrderNoOutput.Result rst = wPosHttpClient.getOrderNo(mid, mid, SysConstants.WX, "", tranAmount, 0, tranAmount, "", "");
		if (rst == null) {
			res.setCode(-1);
			res.setMessage("系统错误，操作失败，请重试");
			log.error("请求开放平台获取订单编号失败， orderId=" + orderId);
			return res;			
		}
	
		String orderNo = rst.getOrderNo();
		if (StringUtils.isEmpty(orderId)) {
			res.setMessage(orderNo);
		} else {
			res.setMessage(orderId);
		}
		// 产生临时订单（获取prepay_id用）
		WxOrder order = new WxOrder();
		order.setOpenId(openId);
		order.setOrderId(orderNo); // 提交给微信的第三方订单号
		order.setAmount(tranAmount);
		
		// 附加数据(支付成功后会返回)
		JSONObject attach = new JSONObject();
		attach.put("mid", mid);
		attach.put("sn", sn);
		attach.put("orderId", orderId);

		// 连接类型1.大商户2渠道3直连
		if ("2".equals(result.getConnectionType())) {// 2渠道
			partnerKey = result.getChannelAppKey();
			appid = result.getChannelAppId();
			partner = result.getChannelPartnerId();
			// 子商户公众账号ID
			String subAppId = result.getMerchantAppId();
			// 子商户号
			String subPartner = result.getMerchantPartnerId();
			// 交易商户支付信息
			if (StringUtils.isEmpty(appid) || StringUtils.isEmpty(partner)
					|| StringUtils.isEmpty(subAppId)
					|| StringUtils.isEmpty(subPartner)
					|| StringUtils.isEmpty(partnerKey)) {
				res.setCode(-1);
				res.setMessage("商户信息不完整，无法发起支付");
				log.error("公众号api支付，渠道商户信息不完整，无法发起支付，mid="+mid);
				log.warn(result);
				return res;
			}
			// 1.生成预支付回话标识
			prepay_id = getJsApiChannelPrepay_id(appid, partner, partnerKey, subAppId, subPartner, notifyUrl,
					httpRequest.getRemoteAddr(), attach.toJSONString(), order, orderItems);		
			
		} else {// 3直连 （此处：1大商户情形不存在）
			partnerKey = result.getMerchantAppKey();
			appid = result.getMerchantAppId();
			partner = result.getMerchantPartnerId();
			// 交易商户支付信息
			if (StringUtils.isEmpty(appid) || StringUtils.isEmpty(partner) || StringUtils.isEmpty(partnerKey)) {
				res.setCode(-1);
				res.setMessage("商户信息不完整，无法发起支付");
				log.error("公众号api支付，直连商户信息不完整，无法发起支付,mid="+mid);
				log.warn(result);
				return res;
			}
			// 1.生成预支付回话标识
			prepay_id = getJsApiPrepay_id(appid, partner, partnerKey, notifyUrl,
					httpRequest.getRemoteAddr(), attach.toJSONString(), order, orderItems);
		}
		
		if (prepay_id == null) {
			res.setCode(-1);
			res.setMessage("请求异常，支付请求失败");
			log.error("公众号api支付，请求异常，支付请求失败, prepay_id = null");
			return res;
		}	

		// 2.支付参数
		WechatPayment pm = new WechatPayment();
		pm.setPaymentPackage("prepay_id=" + prepay_id);
		pm.sign(appid, partnerKey);

		res.setObj(pm);
		return res;
	}

	/**
	 * v3.3.7版本，微信支付结束的回调操作，对订单修改操作
	 * 
	 * @param request
	 * @param response
	 * @throws Exception
	 */
	@RequestMapping(value = "app_pay_notify", method = RequestMethod.POST)
	public void appPayNotification(HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(
				(ServletInputStream) request.getInputStream()));
		String line = null;
		StringBuilder sb = new StringBuilder();
		while ((line = br.readLine()) != null) {
			sb.append(line);
		}
		Map<String, String> resMap = XMLUtil.doXMLParse(sb.toString());
		// 支付结果
		String return_code = resMap.get("return_code");
		String result_code = resMap.get("result_code");
		
		log.error("return_code = " + return_code);
		log.error("result_code = " + result_code);
		
		// 判断签名及结果
		if ("SUCCESS".equals(return_code) && "SUCCESS".equals(result_code)) {
			// 商户订单号
			String payOrderNo = resMap.get("out_trade_no");
			// 金额,以分为单位
			String total_fee = resMap.get("total_fee");
			// 微信支付订单号
			String transactionId = resMap.get("transaction_id");
			// 微信号（用户）
			String openId = resMap.get("sub_openid");
			if (StringUtils.isEmpty(openId)) {
				openId = resMap.get("openid");
			}
			String attach = resMap.get("attach");
			JSONObject attachJson = JSONObject.parseObject(attach);
			String mid = attachJson.getString("mid");
			String sn = attachJson.getString("sn");
			// 微信订单号
			String orderId = attachJson.getString("orderId");
			
			log.debug("out_trade_no="+payOrderNo);
			log.debug("orderId="+orderId);
			log.debug("total_fee="+total_fee);
			log.debug("transactionId="+transactionId);
			log.debug("attach="+attach);
			
			//请求开放平台微信jssdk支付更新接口
			WxJsApiPayUpdateInput input = new WxJsApiPayUpdateInput();
			input.setOrderId(orderId);
			input.setPayOrderNo(payOrderNo);
			input.setTotalFee(total_fee);
			input.setTransactionId(transactionId);
			input.setOpenId(openId);
			input.setMid(mid);
			input.setSn(sn);
			
			WxJsApiPayUpdateOutput output = (WxJsApiPayUpdateOutput) wizarposOpenRestClient.post(input, "/wxshop/wxJsApiPayUpdate", WxJsApiPayUpdateOutput.class);
			WxJsApiPayUpdateOutput.Result order = output.getResult();
			
			if ("60034".equals(output.getCode())) {//订单重复支付			
				log.error("Bad request:重复的支付交易-订单[" + payOrderNo + "]已支付");
				sendToCFT(response,
						"<xml><return_code>FAIL</return_code></xml>");
				return;
			}
			if ("0".equals(output.getCode())) {//操作成功
				log.debug("订单[" + payOrderNo + "]支付成功");
				log.debug("payStatus = " + order.getPayStatus());
			} else {
				log.warn("订单[" + payOrderNo + "] ");
				log.warn("code： " + output.getCode());
				log.warn("message： " + output.getMessage());
			}
			log.debug("success 后台通知成功");
			// 给财付通系统发送成功信息，财付通系统收到此结果后不再进行后续通知
		} else {
			log.error("fail 支付失败");
		}
		sendToCFT(response, "<xml><return_code>SUCCESS</return_code></xml>");
		return;
	}
}
